package com.web_bloks.document.client;

import com.web_bloks.system.client.Logger;

import java.util.Iterator;
import java.util.Vector;

import com.web_bloks.http.client.HttpCall;

import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Header;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;

/**
 * DocumentLoader
 * 
 * A)Fetches 'pages' from the origin server
 *  1) Unconditional GET
 *  2) Conditional   GET
 * B) Notifies 'DocumentLoaderListener'
 * 
 * @author Jean-Lou Dupont
 *
 */
public class DocumentLoader implements RequestCallback {
	
	public final static String DEFAULT_API = "/page/";
	
	public final static int DEFAULT_TIMEOUT = 5000; //5secs
	
	String api = DEFAULT_API;

	HttpCall caller = new HttpCall();
	
	Vector<DocumentLoaderListener> listeners = new Vector<DocumentLoaderListener>();
	
	String current_name = null;
	
	/*===================================================================
	 * CONSTRUCTORS
	 ===================================================================*/
	public DocumentLoader() {
		super();
		setup();
	}
	
	public DocumentLoader(String api) {
		super();
		this.api = api;
		setup();
	}
	protected void setup() {
		caller.setRequestCallback(this);
	}
	
	/*===================================================================
	 * API
	 ===================================================================*/
	public void setAPI(String api) {
		this.api = api;
	}
	/**
	 * Unconditional load
	 */
	public void load(String name, int timeout) throws RequestException {
		caller.setHeader("If-Modified-Since", null );
		_load( name, timeout );
	}
	/**
	 * Unconditional load
	 * @param name
	 * @throws RequestException
	 */
	public void load(String name) throws RequestException {
		caller.setHeader("If-Modified-Since", null );		
		this._load(name, DEFAULT_TIMEOUT );
	}
	/**
	 * Conditional load
	 * @param name
	 * @param LastModified
	 * @throws RequestException
	 */
	public void loadC(String name, String LastModified) throws RequestException{
		loadC( name, LastModified, DEFAULT_TIMEOUT );
	}
	/**
	 * Conditional load
	 * 
	 * @param name
	 * @param LastModified
	 * @param timeout
	 * @throws RequestException
	 */
	public void loadC(String name, String LastModified, int timeout) throws RequestException {
		Logger.logInfo( getClass()+".loadC: start");
		
		caller.setHeader("If-Modified-Since", LastModified);
		_load( name, timeout );
	}
	/**
	 * Conditionally loads a page
	 * 
	 * @param name
	 * @param etag
	 * @throws RequestException
	 */
	public void loadConditionalEtag(String name, String etag) throws RequestException {
		Logger.logInfo( getClass()+".loadConditionalEtag: start");
		
		caller.setHeader("If-None-Match", etag);
		_load( name, DEFAULT_TIMEOUT );
	}
	/**
	 * Conditionally loads a page
	 * 
	 * @param name
	 * @param etag
	 * @param timeout
	 * @throws RequestException
	 */
	public void loadConditionalEtag(String name, String etag, int timeout) throws RequestException {
		Logger.logInfo( getClass()+".loadConditionalEtag: start");
		
		caller.setHeader("If-None-Match", etag);
		_load( name, timeout );
	}
	/**
	 * Performs all the heavy lifting
	 * 
	 * @param name
	 * @param timeout
	 */
	protected void _load(String name, int timeout) throws RequestException {
		Logger.logInfo( getClass()+"._load: start");
		
		if (isPending()) {
			Logger.logWarn( getClass()+"._load: call is progress");
			return;
		}
		this.current_name = new String(name);
		this.caller.setTimeoutMillis(timeout);
		this.caller.execute(this.api + name + '/', null);
	}
	
	public boolean isPending() {
		return caller.isPending();
	}
	
	/*===================================================================
	 * Listeners 
	 ===================================================================*/
	public void addListener( DocumentLoaderListener listener ) {
		listeners.add( listener );
	}
	public void removeListener( DocumentLoaderListener listener ) {
		listeners.remove(listener);
	}
	protected void notifyListeners(Document doc, DocumentLoaderResponse code) {
		//Logger.logInfo( getClass()+".notifyListeners");
		Iterator<DocumentLoaderListener> it = this.listeners.iterator();
		while (it.hasNext()) {
			Object o = it.next ();
		    ((DocumentLoaderListener) o).onDocumentLoaderResponse( doc, code );
		}
	}
	/*========================================================
	 * REQUESTCALLBACK
	 ========================================================*/
	public void onError(Request request, java.lang.Throwable exception ) {
		Logger.logInfo( getClass()+".onError");		
		this.notifyListeners( null, new DocumentLoaderResponse(DocumentLoaderResponse.ERROR) );
	}
	
	public void onResponseReceived(Request request, Response response) {
		
		boolean responseAvailable = response != null;
		int responseStatusCode = response.getStatusCode();
		
		Logger.logInfo( getClass()+".onResponseReceived, response available:" + responseAvailable + ", status code: " + responseStatusCode );
		
		DocumentObject doc = null;
		Title title = null;
		try {
			title = Title.newFromText(current_name);
		} catch(TitleException e) {
			title = null;
		} catch(NamespaceException e) {
			title = null;
		} finally {
			if (null == title) {
				Logger.logWarn(getClass()+".onResponseReceived: error creating doc object");
			}
		}
		// doc defaults to STATE_INVALID
		doc = DocumentObject.createFromTitle( title );
		
		if (responseAvailable && responseStatusCode == 200) {
			
			// only if we also have a valid title
			// This helps ensure that the page won't be cached
			// locally if something is out-of-sync
			if ( null != title )
				doc.setState(Document.STATE_VALID);
			
			Header[] headers = response.getHeaders();
			
			String name = null;
			String value = null;
			
			if (null != headers) {
				for (int i=0;i<headers.length;i++) {
					
					// IE seems to provide some NULL headers
					// from time to time...
					if ( null != headers[i] ) {
						name = headers[i].getName();
						value= headers[i].getValue();
						doc.setMeta(name, value);
					}
					//Logger.logInfo( getClass()+".onResponseReceived: header["+name+"] = " + value );
				}//FOR
			}
			
			doc.setPageContent(response.getText());
			this.notifyListeners( doc, new DocumentLoaderResponse(DocumentLoaderResponse.OK) );			
		} else {
			doc.setState(Document.STATE_INVALID);
			this.notifyListeners( doc, new DocumentLoaderResponse(DocumentLoaderResponse.ERROR, responseStatusCode) );			
		}

	}
	
}//END